﻿using System;
using System.IO;
using System.Threading;
using Synology.DS.Common;
using Synology.DS.Core.DiskStation;
using Synology.DS.Interface.Abstract;
using Synology.DS.Interface.Entities.DiskStation;
using Synology.DS.Interface.Exception;

namespace Synology.DS.Core
{
	public class ProcessingServer : IProcessingServer
	{
		private const string SessionName = "DownloadStation";

		private readonly ITorrentsWatcher _watcher;
		private readonly ITorrentFileTransfomer _tranformer;
		private readonly IConfigurationManager _config;
		private readonly IInstanceResolver _instanceResolver;

		private readonly ServiceInfo _authServiceInfo;
		private readonly ServiceInfo _taskServiceInfo;

		public ProcessingServer(ITorrentsWatcher watcher
			, ITorrentFileTransfomer tranformer
			, IConfigurationManager config
			, IInstanceResolver instanceResolver)
		{
			AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
			_watcher = watcher;
			_tranformer = tranformer;
			_instanceResolver = instanceResolver;
			_config = config;

			_authServiceInfo = _instanceResolver.Resolve<AuthService>().QueryInfo();
			_taskServiceInfo = _instanceResolver.Resolve<TaskService>().QueryInfo();
		}
		

		public void Start()
		{
			Log.Info("Starting server...");
			_watcher.NewTorrent += OnNewTorrent;
			_watcher.Start();
		}

		public void Stop()
		{
			Log.Info("Stopping server...");
			_watcher.NewTorrent -= OnNewTorrent;
			_watcher.Stop();
		}

		public void OnNewTorrent(object sender, NewTorrentEventArgs args)
		{
			string file = args.FilePath;
			try
			{
				//it's not possible to process locked files, lets wait when it become unlocked
				if (FileSystemHelpers.IsFileLocked(file))
				{
					Log.Info("File: '{0}' is locked. Skip it", file);
					return;
				}

				Thread.Sleep(1000); //wait before process file

				var authService = _instanceResolver.Resolve<AuthService>();
				var taskService = _instanceResolver.Resolve<TaskService>();

				string magnetUrl = _tranformer.Transform2Magnet(file);

				string sid = Login(authService);

				AddTask(taskService, magnetUrl, sid);

				Logout(authService);
			   

				Log.Info("Torrent file '{0}' processed successfully", file);
			}
			catch (AccessDiskStationException adse)
			{
				Log.Error(adse, "Problems with accessing DiskStation");
				Thread.Sleep(Const.WaitIntervalIfStationIsUnAccessable);
			}
			catch (Exception ex)
			{
				Log.Error(ex, "Error during processing torrent file: '{0}'", file);
			}
			finally
			{
				ArchiveFile(file);
			}
		}

		public void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
		{
			Log.Error(e.ExceptionObject as Exception, "Unhandled exception");
		}

		private string Login(AuthService authService)
		{
			string password = StringEncoder.Decode(_config.SynologyPassword);
			Log.Info("logging to synology. User: '{0}' Password: '{1}' | Url: '{2}'", _config.SynologyUser, password, _config.SynologyApiUrl);
			return authService.Login(_authServiceInfo, _config.SynologyUser, password, SessionName);
		}

		private void Logout(AuthService authService)
		{
			Log.Info("logging out from synology");
			authService.Logout(_authServiceInfo, SessionName);
		}

		private void AddTask(TaskService taskService, string url, string sid)
		{
			Log.Info("Create download task. Url: '{0}'", url);
			taskService.CreateTask(sid, _taskServiceInfo, url);
		}

		private void ArchiveFile(string file)
		{
			Log.Info("Archiving file: '{0}' to {1}", file, _config.TorrentsArchiveFolder);
			FileSystemHelpers.EnsureDirectoryExistance(_config.TorrentsArchiveFolder);
			FileInfo fInfo = new FileInfo(file);
			File.Move(file, Path.Combine(_config.TorrentsArchiveFolder, fInfo.Name));
		}
	}
}
